/**
 * 
 */
package org.swing.utility.sytem.jar;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;

/**
 * @author lqnhu
 *
 */
public abstract class FlexibleClassLoader extends ClassLoader {
	private final boolean childFirst;
	private final ThreadLocal<Boolean> inGetResourceAsStream = new ThreadLocal<Boolean>();

	public FlexibleClassLoader(ClassLoader parent, boolean childFirst) {
		super(parent);
		this.childFirst = childFirst;
	}

	public FlexibleClassLoader(boolean childFirst) {
		this.childFirst = childFirst;
	}

	@Override
	public URL getResource(String name) {
		if (!childFirst)
			return super.getResource(name);
		URL url = findResource(name);
		if (url == null)
			url = super.getResource(name);
		return url;
	}

	@Override
	public InputStream getResourceAsStream(String name) {
		inGetResourceAsStream.set(Boolean.TRUE);
		try {
			InputStream is = null;
			if (!childFirst)
				is = super.getResourceAsStream(name);
			if (is == null)
				is = findResourceAsStream(name);
			if (is == null && childFirst)
				is = super.getResourceAsStream(name);
			return is;
		} finally {
			inGetResourceAsStream.remove();
		}
	}

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		if (!childFirst)
			return super.loadClass(name, resolve);
		Class c;
		try {
			c = findClass(name);
			if (resolve)
				resolveClass(c);
			return c;
		} catch (ClassNotFoundException e) {
		}
		return super.loadClass(name, resolve);
	}

	@Override
	public Enumeration<URL> getResources(String name) throws IOException {
		Enumeration[] tmp = new Enumeration[2];
		tmp[childFirst ? 1 : 0] = super.getResources(name);
		tmp[childFirst ? 0 : 1] = findResources1(name);
		return new sun.misc.CompoundEnumeration<URL>(tmp);
	}

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		Class<?> clazz = findLoadedClass(name);
		if (clazz == null) {
			final byte[] buf = readResource(name.replace('.', '/') + ".class");
			if (buf == null)
				throw new ClassNotFoundException(name);
			clazz = defineClass(name, buf, 0, buf.length);
		}
		return clazz;
	}

	@Override
	protected URL findResource(String name) {
		if (inGetResourceAsStream.get() == Boolean.TRUE)
			return null;
		return findResource1(name);
	}

	protected abstract URL findResource1(String name);

	protected abstract Enumeration<URL> findResources1(String name);

	protected abstract InputStream findResourceAsStream(String name);

	protected abstract byte[] readResource(String name);
}
